﻿#region Legal Disclaimer
// THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN “AS IS” BASIS, WITHOUT 
// WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT 
// LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, 
// MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE 
// RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH 
// YOU. SHOULD ANY OF THIS SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT 
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY 
// NECESSARY SERVICING, REPAIR OR CORRECTION. NO USE OF THIS SOFTWARE IS 
// AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
// 
// Microsoft Public License (Ms-PL) governs use of this software. If you use the software, 
// you accept this license. If you do not accept the license, do not use the software.
#endregion
using System;
using System.IO;
using System.Reflection;

namespace SimplyConfigured.Sources
{
    /// <summary>
    /// 
    /// </summary>
    public class IniConfigurationSource : IConfigurationSource
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="IniConfigurationSource"/> class.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
        public IniConfigurationSource()
        {
            BackingFilePath =
                new Uri(Path.GetDirectoryName(Assembly.GetAssembly(typeof(IniConfigurationSource)).CodeBase) +
                        "\\App.ini");

            ChangeObserver = new FileSystemChangeObserver(BackingFilePath);
            ChangeObserver.SourceFileChanged += ChangeObserverSourceFileChanged;
            ChangeObserver.Observe();
        }

        void ChangeObserverSourceFileChanged(object sender, ConfigurationSourceFileChangedEventArgs e)
        {
            var configuration = Instance;
            OnConfigurationSourceChanged(new ConfigurationSourceChangedEventArgs(configuration));
        }

        /// <summary>
        /// Gets the change observer.
        /// </summary>
        internal FileSystemChangeObserver ChangeObserver { get; private set; }    //TODO: change to generic List<IFeatureProvider>-type interface to allow for more/different features

        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value></value>
        public IConfiguration Instance
        {
            get
            {
                var iniFile = new IniFileAccess(BackingFilePath);
                var data = iniFile.GetData();
                var config = new SimpleConfiguration();

                foreach (var item in data)
                {
                    config.Add(item.Key, item.Value);
                }

                Data = config;
                return Data;
            }
        }

        /// <summary>
        /// Gets the configuration data for this specific data source.
        /// </summary>
        public IConfiguration Data { private set; get; }

        /// <summary>
        /// Gets or sets the backing file path.
        /// </summary>
        /// <value>
        /// The backing file path.
        /// </value>
        public Uri BackingFilePath { get; set; }

        #region Implementation of IConfigurationSourceObservable

        /// <summary>
        /// Attaches the specified observer.
        /// </summary>
        /// <param name="observer">The observer.</param>
        public void Attach(IConfigurationSourceChangeObserver observer)
        {
            ConfigurationSourceChanged += observer.UpdateConfigurationData;
        }

        /// <summary>
        /// Detaches the specified observer.
        /// </summary>
        /// <param name="observer">The observer.</param>
        public void Detach(IConfigurationSourceChangeObserver observer)
        {
            ConfigurationSourceChanged -= observer.UpdateConfigurationData;
        }

        /// <summary>
        /// Occurs when the underlying configuration data changes.
        /// </summary>
        public event EventHandler<ConfigurationSourceChangedEventArgs> ConfigurationSourceChanged;

        /// <summary>
        /// Called when [configuration data changed].
        /// </summary>
        /// <param name="e">The e.</param>
        public void OnConfigurationSourceChanged(ConfigurationSourceChangedEventArgs e)
        {
            if (ConfigurationSourceChanged != null)
            {
                ConfigurationSourceChanged(this, e);
            }
        }

        #endregion
    }
}